#ifndef MISC_H_
#define MISC_H_

#include <gtk/gtkversion.h>
#include <glibmm.h>
#include <gdkmm.h>
#include <vector>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/utility.hpp>

#include "conf/element.h"
#include "url.h"
#include "gtkversion.h"

#define REGEX_MODULE_GLIB      1
#define REGEX_MODULE_BOOST     2
#define REGEX_MODULE_ONIGURUMA 3

#ifdef USE_BOOST_REGEX
# include <boost/regex/icu.hpp>
# define REGEX_MODULE REGEX_MODULE_BOOST
#elif defined(USE_ONIGURUMA)
# include <oniguruma.h>
# define REGEX_MODULE REGEX_MODULE_ONIGURUMA
#else
# define REGEX_MODULE REGEX_MODULE_GLIB
#endif

#ifdef USE_GLADE
# include <libglademm.h>
#else
# include <gtkmm.h>
#endif

#define ENUM_FLAG_OPERATORS(TYPE)                               \
  inline TYPE operator|(TYPE e1, TYPE e2) {                     \
    return static_cast<TYPE>(static_cast<unsigned>(e1) |        \
                             static_cast<unsigned>(e2));        \
  }                                                             \
  inline TYPE operator&(TYPE e1, TYPE e2) {                     \
    return static_cast<TYPE>(static_cast<unsigned>(e1) &        \
                             static_cast<unsigned>(e2));        \
  }                                                             \
  inline TYPE operator^(TYPE e1, TYPE e2) {                     \
    return static_cast<TYPE>(static_cast<unsigned>(e1) ^        \
                             static_cast<unsigned>(e2));        \
  }                                                             \
  inline TYPE operator~(TYPE e) {                               \
    return static_cast<TYPE>(~static_cast<unsigned>(e));        \
  }                                                             \
  inline TYPE &operator|=(TYPE &e1, TYPE e2) {                  \
    return (e1 = static_cast<TYPE>(static_cast<unsigned>(e1) |  \
                                   static_cast<unsigned>(e2))); \
  }                                                             \
  inline TYPE &operator&=(TYPE &e1, TYPE e2) {                  \
    return (e1 = static_cast<TYPE>(static_cast<unsigned>(e1) &  \
                                   static_cast<unsigned>(e2))); \
  }                                                             \
  inline TYPE &operator^=(TYPE &e1, TYPE e2) {                  \
    return (e1 = static_cast<TYPE>(static_cast<unsigned>(e1) ^  \
                                   static_cast<unsigned>(e2))); \
  }                                                             \


#include "conf/attribute.h"
#include "strfuncs.h"

namespace gpyp {

class autoconn : boost::noncopyable {
private:
  sigc::connection conn_;

public:
  inline sigc::connection *operator->() {
    return &conn_;
  }
  inline const sigc::connection *operator->() const {
    return &conn_;
  }

  inline const sigc::connection &operator=(const sigc::connection &conn) {
    conn_ = conn;
    return conn_;
  }

  inline autoconn() {
  }
  inline autoconn(const sigc::connection &conn) :
    conn_(conn)
  {
  }
  inline virtual ~autoconn() {
    conn_.disconnect();
  }
};

class Color : public Gdk::Color {
private:
  bool none_;

public:
  inline operator bool() const {
    return !none_;
  }
  inline bool operator!() const {
    return none_;
  }

  inline bool set(const Glib::ustring &value) {
    bool res = Gdk::Color::set(value);
    if (res) none_ = false;
    return res;
  }
  inline void set(gushort red, gushort green, gushort blue) {
    Gdk::Color::set_red(red);
    Gdk::Color::set_green(green);
    Gdk::Color::set_blue(blue);
    none_ = false;
  }
  inline void set_red(gushort value) {
    Gdk::Color::set_red(value);
    none_ = false;
  }
  inline void set_green(gushort value) {
    Gdk::Color::set_green(value);
    none_ = false;
  }
  inline void set_blue(gushort value) {
    Gdk::Color::set_blue(value);
    none_ = false;
  }

  inline Color &operator=(const Color &color) {
    none_ = color.none_;
    if (!none_) Gdk::Color::operator=(color);
    return *this;
  }
  inline const Gdk::Color &operator=(const Gdk::Color &color) {
    none_ = false;
    Gdk::Color::operator=(color);
    return color;
  }
  inline const Glib::ustring &operator=(const Glib::ustring &value) {
    set(value);
    return value;
  }

  inline gushort get_brightness() const {
    gushort r = get_red(), g = get_green(), b = get_blue();
    return MAX(MAX(r, g), b);
  }
  inline Color brightness(double ratio) const {
    gushort r = get_red(), g = get_green(), b = get_blue();
    if (ratio < 0) {
      return Color(static_cast<short unsigned>(r + r * ratio),
                   static_cast<short unsigned>(g + g * ratio),
                   static_cast<short unsigned>(b + b * ratio));
    } else if (ratio > 0) {
      return Color(static_cast<short unsigned>(r + (65535 - r) * ratio),
                   static_cast<short unsigned>(g + (65535 - g) * ratio),
                   static_cast<short unsigned>(b + (65535 - b) * ratio));
    } else {
      return *this;
    }
  }

  inline const GdkColor *gobj() const {
    return none_ ? NULL : Gdk::Color::gobj();
  }

  Color() :
    none_(true)
  {
    set_rgb(0, 0, 0);
  }
  Color(const Color &color) :
    Gdk::Color(color),
    none_     (color.none_)
  {
  }
  Color(const Gdk::Color &color) :
    Gdk::Color(color),
    none_     (false)
  {
  }
  Color(const Glib::ustring &value) :
    Gdk::Color(value),
    none_     (false)
  {
  }
  Color(ushort r, ushort g, ushort b) :
    none_ (false)
  {
    set_rgb(r, g, b);
  }
};
inline bool operator==(const Color &c1, const Color &c2) {
  return (!c1 && !c2) || (c1 && c2 && (dynamic_cast<const Gdk::Color &>(c1) ==
                                       dynamic_cast<const Gdk::Color &>(c2)));
}
inline bool operator!=(const Color &c1, const Color &c2) {
  return !(c1 == c2);
}
inline bool operator==(const Color &c1, const Gdk::Color &c2) {
  return c1 && dynamic_cast<const Gdk::Color &>(c1) == c2;
}
inline bool operator!=(const Color &c1, const Gdk::Color &c2) {
  return !(c1 == c2);
}
inline bool operator==(const Gdk::Color &c1, const Color &c2) {
  return c2 == c1;
}
inline bool operator!=(const Gdk::Color &c1, const Color &c2) {
  return c2 != c1;
}

inline std::ostream &operator<<(std::ostream &s, const Color &color) {
  s << format("#%04hu%04hu%04hu",
              color.get_red(),
              color.get_green(),
              color.get_blue());
  return s;
}

namespace conf {
template<>
inline bool PropertyConnection::on_set_value(const Color &color) {
  if (value_type_ == G_TYPE_BOOLEAN) {
    g_object_set(obj_, name_.c_str(), color ? TRUE : FALSE, NULL);
    return true;
  } else if (value_type_ == GDK_TYPE_COLOR) {
    g_object_set(obj_, name_.c_str(), color.gobj(), NULL);
    return true;
  }
  return false;
}
template<> inline void
ConnectionList<Color>::conn_on_changed(PropertyConnection &/*conn*/) {
}
}

class AttributeColor : public conf::Attribute<Color, AttributeColor> {
public:
  static Color to_value(const Glib::ustring &str);
  static Glib::ustring to_string(const Color &value);

  inline AttributeColor(const Glib::ustring &name, const Color &defval) :
    conf::Attribute<Color, AttributeColor>(name, defval)
  {
  }
};

class RefAttributeColor : public conf::RefAttribute<AttributeColor> {
public:
  inline const Color &operator=(const Color &color) {
    (*this)->set_value(color);
    return color;
  }
  inline const Gdk::Color &operator=(const Gdk::Color &color) {
    (*this)->set_value(color);
    return color;
  }
  inline const Glib::ustring &operator=(const Glib::ustring &value) {
    (*this)->set_value(value);
    return value;
  }

  inline RefAttributeColor() {
  }
  inline RefAttributeColor(const Glib::ustring &name,
                           const Color         &defval = Color()) :
    conf::RefAttribute<AttributeColor>(name, defval)
  {
  }
  inline RefAttributeColor(const Glib::ustring &name,
                           const Glib::ustring &defval) :
    conf::RefAttribute<AttributeColor>(name, Color(defval))
  {
  }
};

class AttributeHost : public conf::AttributeStr {
protected:
  Glib::ustring         def_host_;
  uint                  def_port_;

  Glib::ustring         host_;
  uint                  port_;
  boost::logic::tribool valid_;

  virtual void on_changed();

  void update();

  inline static Glib::ustring create_str(const Glib::ustring &host, uint port) {
    return host + ":" + tostr(port);
  }

public:
  inline const Glib::ustring &get_host() const {
    return host_;
  }
  inline void set_host(const Glib::ustring &host) {
    host_ = host;
    set_value(create_str(host_, port_));
  }

  inline uint get_port() const {
    return port_;
  }
  inline void set_port(uint port) {
    port_ = port;
    set_value(create_str(host_, port_));
  }

  inline boost::logic::tribool valid() const {
    return valid_;
  }

  inline bool equal(const URL &url) {
    return (host_ == url.get_host() &&
            static_cast<int>(port_) == url.get_port());
  }

  AttributeHost(const Glib::ustring &name,
                const Glib::ustring &def_host,
                uint                 def_port);
};
typedef conf::RefAttribute<AttributeHost> RefAttributeHost;

class Application;
class FormatTermList;
class Channel;
typedef boost::shared_ptr<Channel> RefChannel;

class AttributeFormat : public conf::AttributeStr {
private:
  boost::shared_ptr<FormatTermList> format_terms_;

protected:
  virtual void on_changed();

public:
  int compare(RefChannel ch1, RefChannel ch2);

  bool format(Glib::ustring &str, RefChannel ch, bool html = false);

  AttributeFormat(const Glib::ustring &name,
                  const Glib::ustring &defval,
                  Application &app);
};
class RefAttributeFormat : public conf::RefAttribute<AttributeFormat> {
public:
  RefAttributeFormat();
  RefAttributeFormat(AttributeFormat *ptr);
  RefAttributeFormat(const Glib::ustring &name,
                     const Glib::ustring &defval,
                     Application &app);
  RefAttributeFormat(const RefAttributeFormat &attr);
};

class AttributeUA : public conf::AttributeStr {
private:
  static Glib::ustring _info;

  Glib::ustring ua_;

protected:
  static Glib::ustring get_distribution(const Glib::ustring &suffix);

  virtual void on_changed();

  void update();

public:
  const Glib::ustring &get_ua();

  AttributeUA(const Glib::ustring &name, const Glib::ustring &defval);
};
typedef conf::RefAttribute<AttributeUA> RefAttributeUA;

enum CLModel {
  CLMODEL_INVALID,
  CLMODEL_DEFAULT,
  CLMODEL_PLAYED,
  CLMODEL_ENDED
};
class AttributeCLModel : public conf::AttributeEnum<CLModel, AttributeCLModel> {
  friend class conf::AttributeEnum<CLModel, AttributeCLModel>;
protected:
  static NameTable _name_table[];

public:
  inline AttributeCLModel(const Glib::ustring &name, CLModel defval) :
    conf::AttributeEnum<CLModel, AttributeCLModel>(name, defval)
  {
  }
};
typedef conf::RefAttributeEnum<AttributeCLModel> RefAttributeCLModel;

/*
namespace conf {
template<>
inline bool PropertyConnection::on_set_value(const Gtk::PositionType &pos) {
  if (value_type_ == GTK_TYPE_POSITION_TYPE) {
    g_object_set(obj_, name_.c_str(), pos, NULL);
    return true;
  }
  return false;
}
}
*/
class AttributePositionType :
    public conf::AttributeEnum<Gtk::PositionType, AttributePositionType> {
  friend class conf::AttributeEnum<Gtk::PositionType, AttributePositionType>;
protected:
  static NameTable _name_table[];

public:
  inline AttributePositionType(const Glib::ustring &name,
                               Gtk::PositionType defval) :
    conf::AttributeEnum<Gtk::PositionType,
                        AttributePositionType>(name, defval)
  {
  }
};
typedef conf::RefAttributeEnum<AttributePositionType> RefAttributePositionType;

enum PanelPosition {
  PANEL_POSITION_INVALID,
  PANEL_POSITION_NONE,
  PANEL_POSITION_LEFT,
  PANEL_POSITION_RIGHT,
  PANEL_POSITION_TOP,
  PANEL_POSITION_BOTTOM,
};
class AttributePanelPos : public conf::AttributeEnum<PanelPosition, AttributePanelPos> {
  friend class conf::AttributeEnum<PanelPosition, AttributePanelPos>;
protected:
  static NameTable _name_table[];

public:
  inline AttributePanelPos(const Glib::ustring &name, PanelPosition defval) :
    conf::AttributeEnum<PanelPosition, AttributePanelPos>(name, defval)
  {
  }
};
typedef conf::RefAttributeEnum<AttributePanelPos> RefAttributePanelPos;

class AttributeAccel : public conf::AttributeStr {
  friend class KeyBindEntry;
public:
  inline static Glib::ustring name(guint key, Gdk::ModifierType mods) {
    Glib::ustring res;
    gchar *name = gtk_accelerator_name(key, (GdkModifierType)mods);
    if (name) {
      res = name;
      g_free(name);
    }
    return res;
  }

private:
  guint             key_;
  Gdk::ModifierType mods_;

protected:
  virtual void on_changed();

public:
  inline guint get_key() const {
    return key_;
  }
  inline Gdk::ModifierType get_mods() const {
    return mods_;
  }

  void set(guint key, Gdk::ModifierType mods);

  inline bool empty() const {
    return key_ == 0;
  }

  AttributeAccel(const Glib::ustring &name, guint key, Gdk::ModifierType mods);
};
typedef conf::RefAttribute<AttributeAccel> RefAttributeAccel;

template<typename RefAttr>
class GObjectPropertyManager {
private:
  inline static void init_gvalue(GValue &value, GType type) {
    memset(&value, 0, sizeof(value));
    g_value_init(&value, type);
  }
  inline static void set_gvalue(GValue &gvalue, int value) {
    init_gvalue(gvalue, G_TYPE_INT);
    g_value_set_int(&gvalue, value);
  }
  inline static void set_gvalue(GValue &gvalue, uint value) {
    init_gvalue(gvalue, G_TYPE_UINT);
    g_value_set_uint(&gvalue, value);
  }
  inline static void set_gvalue(GValue &gvalue, double value) {
    init_gvalue(gvalue, G_TYPE_DOUBLE);
    g_value_set_double(&gvalue, value);
  }
  inline static void set_gvalue(GValue &gvalue, bool value) {
    init_gvalue(gvalue, G_TYPE_BOOLEAN);
    g_value_set_boolean(&gvalue, value);
  }
  inline static void set_gvalue(GValue &gvalue, const Glib::ustring &value) {
    init_gvalue(gvalue, G_TYPE_STRING);
    g_value_set_string(&gvalue, value.c_str());
  }

  GObject      *obj_;
  Glib::ustring name_;
  RefAttr       attr_;

  void attr_on_changed() {
    GValue value;
    set_gvalue(value, *attr_);
    g_object_set_property(obj_, name_.c_str(), &value);
  }

public:
  void apply() {
    attr_on_changed();
  }

  template<typename A1>
  GObjectPropertyManager(A1                  *obj,
                         const Glib::ustring &name,
                         RefAttr              attr) :
    obj_ (G_OBJECT(obj)),
    name_(name),
    attr_(attr)
  {
    attr_on_changed();
    attr->signal_changed()
      .connect(sigc::mem_fun(*this, &GObjectPropertyManager<RefAttr>::attr_on_changed));
  }
};
template<>
void GObjectPropertyManager<conf::RefAttributeStr>::attr_on_changed();

class Regex;
typedef boost::shared_ptr<Regex> RefRegex;

class Regex : boost::noncopyable {
private:
#if (REGEX_MODULE == REGEX_MODULE_GLIB)
  static const Glib::RegexCompileFlags DEFAULT_OPTS;
#endif

  Glib::ustring source_;

#if (REGEX_MODULE == REGEX_MODULE_GLIB)
  Glib::RefPtr<Glib::Regex> re_;
#elif (REGEX_MODULE == REGEX_MODULE_BOOST)
  boost::u32regex re_;
#elif (REGEX_MODULE == REGEX_MODULE_ONIGURUMA)
  OnigRegexType *re_;
#endif

#if (REGEX_MODULE == REGEX_MODULE_BOOST)
  static boost::u32regex boost_create_regex(const Glib::ustring &str,
                                            bool                 caseless);
#endif

public:
  inline static RefRegex create(const Glib::ustring &str,
                                bool                 caseless = false) {
    return RefRegex(new Regex(str, caseless));
  }

  static Glib::ustring escape(const Glib::ustring &str);

public:
  bool match(const Glib::ustring &str);
  bool search(const Glib::ustring &str,
              long                &start,
              long                &length,
              long                 start_pos = 0);
  bool search(const Glib::ustring &str,
              Glib::ustring       &res);
  std::map<long, long> search_all(const Glib::ustring &str);

  inline const Glib::ustring &get_source() const {
    return source_;
  }

protected:
  Regex(const Glib::ustring &str, bool caseless);
public:
  virtual ~Regex();
};

class StatusIconMenu;
class StatusIcon;
typedef boost::shared_ptr<StatusIcon> RefStatusIcon;

#ifndef DISABLE_STATUS_ICON
class StatusIcon : public conf::Element {
private:
  sigc::signal<void> signal_activate_;
  sigc::signal<void, guint, guint32> signal_popup_menu_;
public:
  inline sigc::signal<void> signal_activate() {
    return signal_activate_;
  }

private:
  conf::RefAttributeBool enable_;

  StatusIconMenu *menu_;

#ifdef GTK_HAVE_STATUS_ICON
  Glib::RefPtr<Gtk::StatusIcon> status_icon_;
  Glib::ustring default_icon_name_;
  Glib::ustring icon_name_;

  sigc::connection conn_activate_;
  sigc::connection conn_popup_menu_;
  sigc::connection conn_size_changed_;
#endif

public:
  inline conf::RefAttributeBool enable() {
    return enable_;
  }

private:
#ifdef GTK_HAVE_STATUS_ICON
  void status_icon_on_activate();
  void status_icon_on_popup_menu(guint button, guint32 activate_time);
  bool status_icon_on_size_changed(int size);
  void update_icon();
#endif

public:
#ifdef GTK_HAVE_STATUS_ICON
  inline Glib::RefPtr<Gtk::StatusIcon> get() {
    return status_icon_;
  }
#endif

  void reset_icon();
  void set_icon(const Glib::ustring &icon_name);
  void set_icon(const Gtk::StockID &stock_id);

  void set_tooltip(const Glib::ustring &tooltip_text);
  void set_visible(bool visible = true);

  StatusIcon(Application &app, const Glib::ustring &default_icon_name);
  virtual ~StatusIcon();
};
#endif

enum PrefPage {
  PREF_PAGE_NONE    = -1,
  PREF_PAGE_GENERAL = 0,
  PREF_PAGE_NETWORK,
  PREF_PAGE_COMMAND,
  PREF_PAGE_RECORD,
  PREF_PAGE_VISUAL,
  PREF_PAGE_CHANNELLIST,
  PREF_PAGE_FAVORITE,
  PREF_PAGE_MENU,
  PREF_PAGE_INPUT,
  PREF_PAGE_NOTIFICATION,
  PREF_PAGE_MIGEMO
};

}/*gpyp*/

#endif/*MISC_H_*/
